﻿using System;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Globalization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TVersityDotNet;
using TVersityDotNet.HelperTypes;
using TVersityDotNet.MediaLib;
using TVersityDotNet.Requests;
using TVersityDotNet.Responses;
using TVersityDotNet.Status;
using TVersityDotNetTests.Mocks;

namespace TVersityDotNetTests
{   
    /// <summary>
    ///This is a test class for TversityManagerTest and is intended
    ///to contain all TversityManagerTest Unit Tests
    ///</summary>
    [TestClass()]
    public class TversityManagerTest
    {
        /// <summary>
        /// Lazy loaded value for TVersity's Uri.
        /// </summary>
        private Lazy<Uri> _baseUrl = new Lazy<Uri>( () =>
            {
                return new Uri(ConfigurationManager.AppSettings["TVersityBaseUrl"].ToString(CultureInfo.InvariantCulture)); 
            } );

        private TestContext testContextInstance;
        private TestMessageManager _messageManager;

        /// <summary>
        /// String representing the base url of the TVersity server to test
        /// </summary>
        private Uri BaseUrl
        {
            get
            {
                // Lazy load the configuration                           
                return this._baseUrl.Value;
            }
        }

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        [TestInitialize()]
        public void MyTestInitialize()
        {
            _messageManager = new TestMessageManager();
        }
        
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for TversityManager Constructor
        ///</summary>
        [TestMethod()]
        public void TversityManagerConstructorTest()
        {   
            TVersityManager target = new TVersityManager(this.BaseUrl);
			Assert.AreEqual(this.BaseUrl, target.BaseUrl);
        }

        /// <summary>
        ///A test for MediaSourceFetchList
        ///</summary>
        [TestMethod()]
        public void MediaSourceFetchListTest()
        {
			_messageManager.SetTestCase(true);
            TVersityManager target = new TVersityManager(_messageManager);
            MediaSourceType type = MediaSourceType.Folder;
            Collection<MediaSource> expected = new Collection<MediaSource>(); // TODO: Initialize to an appropriate value
            Collection<MediaSource> actual;
            actual = target.MediasourceFetchList(type);
            Assert.AreEqual(4, actual.Count, "Did not return proper number of Media Sources");            
        }

        /// <summary>
        ///A test for MediasourceRefresh
        ///</summary>
        [TestMethod()]
        public void MediasourceRefreshTest()
        {
			_messageManager.SetTestCase(true);
            TVersityManager target = new TVersityManager(_messageManager);
            var sources = target.MediasourceFetchList(MediaSourceType.Folder);
            MediaSource mediaSource = null;
            if (sources.Count >0 )
                mediaSource = target.MediasourceFetchList(MediaSourceType.Folder)[0];
            bool expected = true;
            if (mediaSource == null)
                Assert.Fail("Could not retrieve media sources");                        
            
            var actual = target.MediasourceRefresh(mediaSource);
            Assert.AreEqual(expected, actual, "Refresh operation failed");            
        }

        /// <summary>
        ///A test for MediasourceRefreshId
        ///</summary>
        [TestMethod()]
        public void MediasourceRefreshTestById()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            var sources = target.MediasourceFetchList(MediaSourceType.Folder);
            MediaSource mediaSource = null;
            if (sources.Count > 0)
                mediaSource = target.MediasourceFetchList(MediaSourceType.Folder)[0];
            bool expected = true;
            bool actual;
            if (mediaSource == null)
                Assert.Fail("Could not retrieve media sources");

            actual = target.MediasourceRefresh(mediaSource.Id);
            Assert.AreEqual(expected, actual, "Refresh operation failed");
        }

        /// <summary>
        ///A test for CacheDeleteAll
        ///</summary>
        [TestMethod()]
        public void CacheDeleteAllTest()
        {            
            TVersityManager target = new TVersityManager(this.BaseUrl); // TODO: Initialize to an appropriate value
            bool expected = true;
            bool actual;
            actual = target.CacheDeleteAll();
            Assert.AreEqual(expected, actual, "Cache delete failed");            
        }

        /// <summary>
        ///A test for MediasourceRefreshAll
        ///</summary>
        [TestMethod()]
        public void MediasourceRefreshAllTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            bool expected = true;
            bool actual;
            actual = target.MediasourceRefreshAll();
            Assert.AreEqual(expected, actual, "Full refresh did not succeed");            
        }

        /// <summary>
        ///A test for Proxy
        ///</summary>
        [TestMethod()]
        public void ProxyTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            MediaSourceFetchListRequest request = new MediaSourceFetchListRequest(MediaSourceType.Folder);
            Collection<MediaSource> actual = target.MediasourceFetchList(MediaSourceType.Folder);
            MediaSourceFetchListResponse proxyActual = null;
            proxyActual = new MediaSourceFetchListResponse(target.Proxy(request).ResponseXml);
            Assert.IsTrue(actual.Count > 0, "Response failed.");
            Assert.IsTrue(proxyActual.ResponseSuccess, "Response failed.");
            Assert.AreEqual(actual.Count, proxyActual.ListOfSources.Count, "Did not return proper number of sources");
            Assert.AreEqual(typeof(MediaSourceFetchListResponse), proxyActual.GetType(), "Did not return proper Response Type");                        
        }

        /// <summary>
        /// A test for Proxy - Send a ProxyRequest as request type (should throw an exception)
        ///</summary>
        [TestMethod()]        
        public void ProxyTest_ProxyRequestType()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);            
            ProxyRequest request = new ProxyRequest(new TVersityRequest(), this.BaseUrl);
            TVersityResponse actual = null;
            try
            {
                actual = target.Proxy(request);
            }
            catch (InvalidOperationException ex)
            {
                Assert.IsInstanceOfType(ex, typeof(InvalidOperationException), "Exception was not of proper type");
                Assert.IsTrue(ex.Message.ToUpper().Contains("Could not load proxy: TVersityRequest cannot be of type ProxyRequest".ToUpper()), "Invalid exception thrown");
                return;
            }
            catch (Exception)
            {
                Assert.Fail("Improper exception type was thrown");
            }

            Assert.Fail("Did not throw exception");
        }

        /// <summary>
        ///A test for DescriptionFetch
        ///</summary>
        [TestMethod()]
        public void DescriptionFetchTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);            
            DescriptionFetchResponse actual;
            actual = target.DescriptionFetch();
            Assert.IsNotNull(actual, "Returned a null response");
            Assert.IsTrue(actual.ResponseSuccess, "Request failed with message: " + actual.ResponseMessage);                        
        }

        /// <summary>
        ///A test for SettingsFetch
        ///</summary>
        [TestMethod()]
        public void SettingsFetchTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);            
            SettingsFetchResponse actual;
            actual = target.SettingsFetch();
            Assert.IsTrue(actual.ResponseSuccess, "Operation did not succeed");
            Assert.AreEqual(37, actual.Settings.PropertyBag.Count, "Did not return proper number of properties");
        }

        /// <summary>
        ///A test for SettingsUpdate - Update with all values, w/o changing anything
        ///</summary>
        [TestMethod()]
        public void SettingsUpdateTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);     
            bool updateBaseUrlOnPortChange = false;
            TVSettings settings = target.SettingsFetch().Settings ;            
            TVersityResponse actual;
            actual = target.SettingsUpdate(settings, updateBaseUrlOnPortChange);
            Assert.IsTrue(actual.ResponseSuccess, "Request failed");
            TVSettings settings2 = target.SettingsFetch().Settings;
            Assert.AreEqual(settings2.PropertyBag.Count, settings.PropertyBag.PropertiesWithValues.Count, "Did not return same number of properties");
            foreach (string key in settings.PropertyBag.PropertiesWithValues.Keys)
            {
                Assert.IsTrue(settings2.PropertyBag.ContainsKey(key, true), "Missing key: " + key);
                Assert.AreEqual(settings.PropertyBag.GetPropertyFromPropertyBag(key), settings2.PropertyBag.GetPropertyFromPropertyBag(key), 
                    string.Format(CultureInfo.InvariantCulture, "Values for {0} did not match.", key));
            }
        }

        /// <summary>
        ///A test for SettingsUpdate - Update with all values, changing connection quality
        ///</summary>
        [TestMethod()]
        public void SettingsUpdateTest_ChangeConnectionQuality()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            bool updateBaseUrlOnPortChange = false;
            TVSettings settings = target.SettingsFetch().Settings;
            ConnectionQualityOption currentQuality = settings.ConnectionQuality;
            if (currentQuality == ConnectionQualityOption.High)
                settings.ConnectionQuality = ConnectionQualityOption.Low;
            else
                settings.ConnectionQuality = ConnectionQualityOption.High;

            TVersityResponse actual;
            actual = target.SettingsUpdate(settings, updateBaseUrlOnPortChange);
            Assert.IsTrue(actual.ResponseSuccess, "Request failed");
            TVSettings settings2 = target.SettingsFetch().Settings;
            Assert.AreEqual(settings2.PropertyBag.Count, settings.PropertyBag.Count, "Did not return same number of properties");
            foreach (string key in settings.PropertyBag.PropertiesWithValues.Keys)
            {
                Assert.IsTrue(settings2.PropertyBag.ContainsKey(key, true), "Missing key: " + key);
                Assert.AreEqual(settings.PropertyBag.GetPropertyFromPropertyBag(key), settings2.PropertyBag.GetPropertyFromPropertyBag(key), string.Format(CultureInfo.InvariantCulture, "Values for {0} did not match.", key));
            }

            // Cleanup
            settings.ConnectionQuality = currentQuality;
            actual = target.SettingsUpdate(settings, updateBaseUrlOnPortChange);
            Assert.IsTrue(actual.ResponseSuccess, "Cleanup failed: Please set connection quality back to: " + Enum.GetName(typeof(ConnectionQualityOption), currentQuality));
        }

        /// <summary>
        ///A test for MediasourceResetAll
        ///</summary>
        [TestMethod()]
        public void MediasourceResetAllTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl); 
            bool expected = true;
            bool actual;
            actual = target.MediasourceResetAll();
            Assert.AreEqual(expected, actual, "Request failed");            
        }

        /// <summary>
        ///A test for MediasourceFetch
        ///</summary>
        [TestMethod()]
        public void MediasourceFetchTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            Collection<MediaSource> expectedList = target.MediasourceFetchList(MediaSourceType.Folder);
            var expected = new MediaSource();
            if (expectedList.Count > 0)
                expected = expectedList[0];
            else
                Assert.Fail("Could not retreive any MediaSources");
                        
            MediaSource actual;
            actual = target.MediasourceFetch(expected.Id);
            Assert.IsNotNull(actual, "Response came back null");
            Assert.AreEqual(expected.Id, actual.Id, "Ids did not match");
            Assert.AreEqual(expected.LookForAudio, actual.LookForAudio, "LookForAudio did not match");
            Assert.AreEqual(expected.LookForPhoto, actual.LookForPhoto, "LookForPhoto did not match");
            Assert.AreEqual(expected.LookForVideo, actual.LookForVideo, "LookForVideo did not match");
            Assert.AreEqual(expected.Name, actual.Name, "Names did not match");
            Assert.AreEqual(expected.Public, actual.Public, "Public did not match");            
        }

        /// <summary>
        ///A test for MediasourcePoliteFetchList
        ///</summary>
        [TestMethod()]
        public void MediasourcePoliteFetchListTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            MediaSourceType type = MediaSourceType.Folder;
            Collection<MediaSource> expected = target.MediasourceFetchList(type);
            
            // Force the DB to lock by adding a new MediaSource
            MediaSource tempSource = expected[0];
            tempSource.Name = tempSource.Name + "_TEMP";
            if (target.MediasourceAdd(tempSource) == 0)            
                Assert.Fail("Adding new MediaSource failed");

            Collection<MediaSource> actual;
            actual = target.MediasourcePoliteFetchList(type);
            Assert.AreEqual(expected.Count, actual.Count, "Did not return expected number of MediaSources");
        
            // Cleanup 
            while (expected.Count == actual.Count)
            {                
                System.Threading.Thread.Sleep(5000);
                actual = target.MediasourcePoliteFetchList(type);
            }

            foreach (MediaSource ms in actual)
            {
                if (string.Compare(ms.Name, tempSource.Name, true) == 0)
                {
                    Assert.IsTrue(target.MediasourceDelete(ms.Id), "Cleanup failed: Could not delete temp media source");
                    break;
                }
            }
        }

        /// <summary>
        ///A test for MediasourceFetchListUpdateId
        ///</summary>
        [TestMethod()]
        public void MediasourceFetchListUpdateIdTest()
        {            
            TVersityManager target = new TVersityManager(this.BaseUrl); 
            int actual;
            actual = target.MediasourceFetchListUpdateId(MediaSourceType.Folder);
            Assert.AreNotEqual(0, actual, "Did not return proper integer");            
        }

        /// <summary>
        ///A test for MedialibSearch
        ///</summary>
        [TestMethod()]
        public void MedialibSearchTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            //SearchOptions searchOptions = new SearchOptions("Search query");
            SearchOptions searchOptions = new SearchOptions("Myth");
            searchOptions.StartAt = 0;
            searchOptions.Count = 50;
            MediaLibResponse response = target.MedialibSearch(searchOptions);

            foreach (MediaLibResponseItem sri in response.ResponseItems)
            {
                Console.WriteLine(sri.Title);
                Console.WriteLine(sri.Id);
                //etc
            }

            foreach (MediaLibMusicItem smi in response.MusicItems)
            {
                Console.WriteLine(smi.Title);
            }
            foreach (MediaLibVideo smi in response.Videos)
            {
                Console.WriteLine(smi.Title);
                Console.WriteLine(smi.Genre);
                // etc
            }

            foreach (MediaLibMusicArtist artist in response.MusicArtists)
            {
                Console.WriteLine(artist.Title);
            }

            foreach (MediaLibPhoto photo in response.Photos)
            {
                Console.WriteLine(photo.Title);
            }

            Assert.Inconclusive("Verify the correctness of this test method.");
        }

        /// <summary>
        ///A test for StatusFetch
        ///Properly unit testing this would require a specific TVersity instance, in a known state.
        ///For now, if it doesn't throw an exception, we'll call it ok.
        ///</summary>
        [TestMethod()]
        public void StatusFetchTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);            
            TVStatus actual = target.StatusFetch();            
            Assert.IsNotNull(actual, "Returned a null response");            
        }

        /// <summary>
        ///A test for MedialibBrowse
        ///</summary>
        [TestMethod()]
        public void MedialibBrowseTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            BrowseOptions browseOptions = new BrowseOptions(); 
            MediaLibResponse actual = target.MedialibBrowse(browseOptions);

            Assert.IsNotNull(actual, "Response came back null");
            Assert.IsNotNull(actual.Containers, "Did not return any containers");
            
            MediaLibResponse subContainer = target.MedialibBrowse(new BrowseOptions(actual.Containers[0]));

            // Using title
            var bo = new BrowseOptions();
            bo.UseTitle(subContainer.Containers[0].Title, subContainer.Containers[0].ParentId);
            var response = target.MedialibBrowse(bo);

            Assert.IsNotNull(subContainer, "Response came back null");
            Assert.IsNotNull(subContainer.Containers, "Did not return any containers");

            Assert.AreEqual(actual.Containers[0].Id, subContainer.Containers[0].ParentId, "Did not return proper child");                        
        }

        /// <summary>
        ///A test for MediasourceUpdate
        ///</summary>
        [TestMethod()]
        public void MediasourceUpdateTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            var initialSource = target.MediasourceFetchList(MediaSourceType.Folder)[0];            
            var copy = target.MediasourceFetchList(MediaSourceType.Folder)[0];

            copy.Public = !initialSource.Public;
            
            bool success = target.MediasourceUpdate(copy);
            Assert.IsTrue(success, "Update request failed");

            var updated = target.MediasourceFetch(initialSource.Id);
            Assert.AreEqual(updated.Public, !initialSource.Public, "Public update failed");
            Assert.IsTrue(target.MediasourceUpdate(initialSource), "Final update failed");
        }

        /// <summary>
        ///A test for MediasourceAdd
        ///</summary>
        [TestMethod()]
        public void MediasourceAddTest()
        {
            TVersityManager target = new TVersityManager(this.BaseUrl);
            MediaSource mediaSource = new MediaSource(0, string.Format(@"\\{0}\C$", this.BaseUrl.Host) , MediaSourceType.Folder, "Testing123", new Collection<string>(), true);            
            int newId = target.MediasourceAdd(mediaSource);
            Assert.IsTrue(newId > 0 , "Response failed");
            Assert.IsTrue(target.MediasourceDelete(target.MediasourceFetch(newId)), "Delete failed");
        }
    }
}
